package com.niiwoo.civet.trade.service.local.databackflow;

import com.dangdang.ddframe.rdb.sharding.api.HintManager;
import com.dangdang.ddframe.rdb.sharding.hint.HintManagerHolder;
import com.niiwoo.civet.trade.constant.LoanFlowMqConstant;
import com.niiwoo.civet.trade.dao.entity.DataBackflowStatus;
import com.niiwoo.civet.trade.dao.entity.Project;
import com.niiwoo.civet.trade.dao.entity.ProjectFlowExecute;
import com.niiwoo.civet.trade.dao.entity.ProjectFlowExecuteRuntime;
import com.niiwoo.civet.trade.dao.mapper.DataBackflowStatusMapperExt;
import com.niiwoo.civet.trade.dao.mapper.ProjectFlowExecuteMapperExt;
import com.niiwoo.civet.trade.dao.mapper.ProjectFlowExecuteRuntimeMapperExt;
import com.niiwoo.civet.trade.dao.mapper.ProjectMapperExt;
import com.niiwoo.civet.trade.dto.request.ProjectFlowRequestDTO;
import com.niiwoo.civet.trade.enums.*;
import com.niiwoo.civet.trade.service.local.databackflow.listener.DataBackflowListeners;
import com.niiwoo.civet.trade.service.local.flowexecute.ProjectFlowExecuteService;
import com.niiwoo.civet.trade.service.local.loan.LoanReturnService;
import com.niiwoo.civet.trade.service.project.ProjectDubboService;
import com.niiwoo.tripod.service.component.SnowflakeIdWorker;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * @author wping
 * @Description: 数据回流服务类
 * @date 2018/4/16 9:35
 */
@Slf4j
@Service
public class DataBackflowService {

    // 回流超时时间(秒)
    public static final int BACKFLOW_TIMEOUT_SECONDS = 36000;

    // 执行次数阈值
    public static final int EXECUTE_TIMES_THRESHOLD = 3;

    private static final String DATA_BACKFLOW_SUCCESS_LOCK = "dataBackflowSuccessLock-";

    // 阻塞流程执行的回流回流信息
    public static final List<DataBackflowStatusDataTypeEnum> BLOCK_EXECUTE_BACKFLOW = Arrays.asList(DataBackflowStatusDataTypeEnum.ADDRESS_LIST, DataBackflowStatusDataTypeEnum.IDENTITY_REAL_INFO,
            DataBackflowStatusDataTypeEnum.LOAN_INFO, DataBackflowStatusDataTypeEnum.PERSONAL_INFO, DataBackflowStatusDataTypeEnum.PROFESSION_INFO,
            DataBackflowStatusDataTypeEnum.BANK_INFO);

    @Autowired
    private DataBackflowStatusService DataBackflowStatusService;

    @Autowired
    private DataBackflowStatusMapperExt dataBackflowStatusMapperExt;

    @Autowired
    private ProjectFlowExecuteMapperExt projectFlowExecuteMapperExt;

    @Autowired
    private SnowflakeIdWorker snowflakeIdWorker;

    @Autowired
    private ProjectFlowExecuteService projectFlowExecuteService;

    @Autowired
    private LoanReturnService loanReturnService;

    @Autowired
    private ProjectFlowExecuteRuntimeMapperExt projectFlowExecuteRuntimeMapperExt;

    @Autowired
    private ProjectMapperExt projectMapperExt;

    @Autowired
    private DataBackflowListeners dataBackflowListeners;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private ProjectDubboService projectDubboService;

    public void executeDataBackflow(Long flowExecuteId) {

        String methodName = "执行借款数据回流请求, flowExecuteId:" + flowExecuteId;
        if (!HintManagerHolder.isMasterRouteOnly()) {
            HintManagerHolder.clear();
            HintManager hintManager = HintManager.getInstance();
            hintManager.setMasterRouteOnly();
        }
        ProjectFlowExecute projectFlowExecute = projectFlowExecuteMapperExt.selectByPrimaryKey(flowExecuteId);
        if (projectFlowExecute == null) {
            log.error(methodName + "-失败，ProjectFlowExecute记录不存在");
            return;
        }

        if (projectFlowExecute.getStatus() != null && ProjectFlowExecuteStatusEnum.EXECUTING.getStatus() != projectFlowExecute.getStatus().byteValue()) {
            log.info(methodName + "-流程已经结束，退出不更新");
            return;
        }

        DataBackflowService dataBackflowService = (DataBackflowService) AopContext.currentProxy();

        if (!HintManagerHolder.isMasterRouteOnly()) {
            HintManagerHolder.clear();
            HintManager hintManager = HintManager.getInstance();
            hintManager.setMasterRouteOnly();
        }
        ProjectFlowExecuteRuntime projectFlowExecuteRuntime = projectFlowExecuteRuntimeMapperExt.selectByPrimaryKey(flowExecuteId);
        if (projectFlowExecuteRuntime == null) {
            log.error(methodName + "-失败，ProjectFlowExecuteRuntime记录不存在");
            dataBackflowService.failEndProjectFlowExecuteAndReturnProject(flowExecuteId, projectFlowExecute.getProjectId(), ReturnProjectReasonTypeEnum.OTHER_EXCEPTION, "ProjectFlowExecuteRuntime记录不存在");
            return;
        }

        if (LoanNodeTypeEnum.LOAN_DATA_BACK_FLOW.getId() != projectFlowExecuteRuntime.getNodeId().intValue()) {
            log.error(methodName + "-失败，当前节点并非借款数据回流，ProjectFlowExecuteRuntime.nodeId:{}", projectFlowExecuteRuntime.getNodeId());
            return;
        }

        String userId = projectFlowExecute.getBorrowUserId();
        Long projectId = projectFlowExecute.getProjectId();
        methodName += ", userId:" + userId + ", projectId:" + projectId;
        // 判断是否所有阻塞流程执行的回流信息节点都初始化成功-存在未初始化的节点流程直接结束流程，退回标的
        if (!isAllBlockExecuteExists(userId, projectId)) {
            log.error(methodName + "-失败，结束整个流程, 退回标的, 存在未初始化的阻塞流程回流节点");
            dataBackflowService.failEndProjectFlowExecuteAndReturnProject(flowExecuteId, projectFlowExecute.getProjectId(), ReturnProjectReasonTypeEnum.OTHER_EXCEPTION, "存在未初始化的阻塞流程回流节点");
            return;
        }

        // 判断标的状态
        if (!HintManagerHolder.isMasterRouteOnly()) {
            HintManagerHolder.clear();
            HintManager hintManager = HintManager.getInstance();
            hintManager.setMasterRouteOnly();
        }
        Project project = projectMapperExt.selectByPrimaryKey(projectId);
        if (project == null) {
            log.error(methodName + "-标的不存在");
            dataBackflowService.failEndProjectFlowExecute(flowExecuteId, "标的不存在");
            return;
        }

        if (!(project.getStatus().equals(ProjectStatusEnum.CHECKING.getStatus())
                && project.getSubStatus().equals(ProjectSubStatusEnum.CHECKING_INIT.getSubStatus()))) {
            log.error(methodName + "-标的状态不在审核中, 当前标的状态:{}", project.getStatus());
            dataBackflowService.failEndProjectFlowExecute(flowExecuteId, "标的状态不在审核中, 当前标的状态:" + project.getStatus());
            return;
        }

        // 判断是否所有回流都成功
        if (isAllBlockExecuteBackflowSuccess(userId, projectId)) {
            log.info(methodName + "-检测到所有阻塞流程的回流节点回流成功, 触发授权状态检测");
            projectFlowExecuteService.initNextStepProjectFlowExecute(flowExecuteId, LoanNodeTypeEnum.LOAN_DATA_BACK_FLOW, LoanNodeTypeEnum.AUTH_STATUS_CHECK, LoanFlowMqConstant.Queue.AUTH_STATUS_CHECK_QUEUE);
            return;
        }

        int currentExecuteTimes = projectFlowExecuteRuntime.getRetryTimes() == null ? 1 : projectFlowExecuteRuntime.getRetryTimes() + 1;

        // 通知各个借款回流节点执行回流动作
        dataBackflowListeners.onNotify(flowExecuteId, userId, projectId, currentExecuteTimes);

    }

    /**
     * 数据回流结束流程并且退回标的
     *
     * @param flowExecuteId
     * @param projectId
     * @param reasonTypeEnum
     * @param errMsg
     * @return
     */
    @Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
    public boolean failEndProjectFlowExecuteAndReturnProject(Long flowExecuteId, Long projectId, ReturnProjectReasonTypeEnum reasonTypeEnum, String errMsg) {
        boolean result = failEndProjectFlowExecute(flowExecuteId, errMsg);

        if (!HintManagerHolder.isMasterRouteOnly()) {
            HintManagerHolder.clear();
            HintManager hintManager = HintManager.getInstance();
            hintManager.setMasterRouteOnly();
        }
        Project project = projectMapperExt.selectByPrimaryKey(projectId);
        if (project != null && project.getFromSource() != null && ProjectFromSourceEnum.HUIJIE.getType() == project.getFromSource().byteValue()) {
            ProjectFlowRequestDTO projectFlowRequestDTO = new ProjectFlowRequestDTO();
            projectFlowRequestDTO.setProjectId(projectId);
            projectFlowRequestDTO.setSubStatus(ProjectSubStatusEnum.MISCARRY_CANCER_BORROWER);
            projectDubboService.flow(projectFlowRequestDTO);
            //TODO GWP 流标通知汇介asset端
        } else {
            returnProject(projectId, reasonTypeEnum);
        }
        return result;
    }

    public boolean failEndProjectFlowExecute(Long flowExecuteId, String errMsg) {
        return projectFlowExecuteService.failEndProjectFlowExecute(flowExecuteId, LoanNodeTypeEnum.LOAN_DATA_BACK_FLOW, errMsg);
    }

    public void returnProject(Long projectId, ReturnProjectReasonTypeEnum reasonTypeEnum) {
        loanReturnService.returnProject(projectId, ProjectSubStatusEnum.CHECKING_BACK_ADDRESSLIST_TIMEOUT, reasonTypeEnum);
    }

    /**
     * 更新数据回流状态并通知回流状态有更新
     *
     * @param flowExecuteId       数据回流ID
     * @param isBackflowSuccess   数据回流是否成功
     * @param dataBackflowStatus  数据回流DTO
     * @param currentExecuteTimes 当前流程执行次数
     * @param desc                描述(成功、失败原因)
     */
    public void updateAndNotifyBackflowStatusChange(Long flowExecuteId, boolean isBackflowSuccess, DataBackflowStatus dataBackflowStatus, int currentExecuteTimes, String desc) {
        updateAndNotifyBackflowStatusChange(flowExecuteId, isBackflowSuccess, dataBackflowStatus, BACKFLOW_TIMEOUT_SECONDS, EXECUTE_TIMES_THRESHOLD, currentExecuteTimes, desc);
    }

    /**
     * 更新数据回流状态并通知回流状态有更新
     *
     * @param flowExecuteId          数据回流ID
     * @param isBackflowSuccess      数据回流是否成功
     * @param dataBackflowStatus     数据回流DTO
     * @param backflowTimeoutSeconds 数据回流超时时间（从第一次回流开始后算）
     * @param executeTimesThreshold  数据回流重试次数阈值
     * @param currentExecuteTimes    当前流程执行次数
     * @param desc                   描述(成功、失败原因)
     */
    public void updateAndNotifyBackflowStatusChange(Long flowExecuteId, boolean isBackflowSuccess, DataBackflowStatus dataBackflowStatus, int backflowTimeoutSeconds, int executeTimesThreshold, int currentExecuteTimes, String desc) {
        // 更新数据回流状态
        DataBackflowStatusService.updateDataBackflowStatus(isBackflowSuccess, dataBackflowStatus, backflowTimeoutSeconds, executeTimesThreshold, desc);
        DataBackflowService dataBackflowService = (DataBackflowService) AopContext.currentProxy();
        // 判断是否是阻塞流程的节点发生状态变更，是则通知状态变更，否则不处理
        for (DataBackflowStatusDataTypeEnum dataTypeEnum : BLOCK_EXECUTE_BACKFLOW) {
            if (dataBackflowStatus.getDataType() != null && dataTypeEnum.getType() == dataBackflowStatus.getDataType().byteValue()) {
                // 通知数据回流状态发生变化
                dataBackflowService.notifyBackflowStatusChange(flowExecuteId, dataBackflowStatus, currentExecuteTimes);
            }
        }
    }

    /**
     * 判断回流节点是否回流成功
     *
     * @param userId       用户ID
     * @param projectId    标的ID
     * @param dataTypeEnum 回流类型
     * @return 回流成功返回true, 否则返回false
     */
    public boolean isBackflowSuccess(String userId, Long projectId, DataBackflowStatusDataTypeEnum dataTypeEnum) {
        if (StringUtils.isEmpty(userId) || projectId == null || dataTypeEnum == null) {
            log.error("判断回流节点是否回流成功-失败，userId/projectId/dataTypeEnum为空");
            return false;
        }
        if (!HintManagerHolder.isMasterRouteOnly()) {
            HintManagerHolder.clear();
            HintManager hintManager = HintManager.getInstance();
            hintManager.setMasterRouteOnly();
        }
        DataBackflowStatus dataBackflowStatus = dataBackflowStatusMapperExt.selectByUserIdProjectIdWithDataType(userId, projectId, dataTypeEnum.getType());
        if (dataBackflowStatus != null && dataBackflowStatus.getDataStatus() != null && DataBackflowStatusDataStatusEnum.BACKFLOW_SUCCESSED.getStatus() == dataBackflowStatus.getDataStatus().byteValue()) {
            log.info("判断回流节点是否回流成功-回流成功, dataTypeEnum:{}", dataTypeEnum.getDesc());
            return true;
        } else {
            log.info("判断回流节点是否回流成功-记录不存在或者回流未完成, dataTypeEnum:{}", dataTypeEnum.getDesc());
            return false;
        }
    }

    /**
     * 判断是否所有阻塞流程的回流信息都已经回流成功
     *
     * @param userId    用户ID
     * @param projectId 标的ID
     * @return 都回流成功返回true, 否则返回false
     */
    public boolean isAllBlockExecuteBackflowSuccess(String userId, Long projectId) {

        if (StringUtils.isEmpty(userId) || projectId == null) {
            log.error("判断是否所有阻塞流程执行的回流信息节点都回流成功-失败，userId/projectId为空");
            return false;
        }

        for (DataBackflowStatusDataTypeEnum dataTypeEnum : BLOCK_EXECUTE_BACKFLOW) {
            if (!HintManagerHolder.isMasterRouteOnly()) {
                HintManagerHolder.clear();
                HintManager hintManager = HintManager.getInstance();
                hintManager.setMasterRouteOnly();
            }
            DataBackflowStatus dataBackflowStatus = dataBackflowStatusMapperExt.selectByUserIdProjectIdWithDataType(userId, projectId, dataTypeEnum.getType());
            if (dataBackflowStatus == null || (dataBackflowStatus.getDataStatus() != null && DataBackflowStatusDataStatusEnum.BACKFLOW_SUCCESSED.getStatus() != dataBackflowStatus.getDataStatus().byteValue())) {
                log.info("判断是否所有阻塞流程执行的回流信息节点都回流成功-有未执行完成记录(记录不存在或者回流未完成), dataTypeEnum:{}", dataTypeEnum.getDesc());
                return false;
            }
        }
        return true;
    }

    /**
     * 判断是否所有阻塞流程执行的回流信息节点都初始化成功
     *
     * @param userId    用户ID
     * @param projectId 标的ID
     * @return 都存在返回true, 否则返回false
     */
    public boolean isAllBlockExecuteExists(String userId, Long projectId) {
        if (StringUtils.isEmpty(userId) || projectId == null) {
            log.error("判断是否所有阻塞流程执行的回流信息节点都初始化成功-失败，userId/projectId非空");
            return false;
        }

        for (DataBackflowStatusDataTypeEnum dataTypeEnum : BLOCK_EXECUTE_BACKFLOW) {
            if (!HintManagerHolder.isMasterRouteOnly()) {
                HintManagerHolder.clear();
                HintManager hintManager = HintManager.getInstance();
                hintManager.setMasterRouteOnly();
            }
            DataBackflowStatus dataBackflowStatus = dataBackflowStatusMapperExt.selectByUserIdProjectIdWithDataType(userId, projectId, dataTypeEnum.getType());
            if (dataBackflowStatus == null) {
                log.info("判断是否所有阻塞流程执行的回流信息节点都初始化成功-存在记录不存在, dataTypeEnum:{}", dataTypeEnum.getDesc());
                return false;
            }
        }
        return true;
    }

    /**
     * 通知数据回流状态有更新
     *
     * @param flowExecuteId       数据回流ID
     * @param dataBackflowStatus  数据回流DTO
     * @param currentExecuteTimes 当前流程执行次数
     */
    @Transactional(rollbackFor = Exception.class)
    public void notifyBackflowStatusChange(Long flowExecuteId, DataBackflowStatus dataBackflowStatus, int currentExecuteTimes) {

        if (flowExecuteId == null || dataBackflowStatus == null || dataBackflowStatus.getDataStatus() == null) {
            log.error("收到数据回流状态发生变更通知，异常通知信息，数据回流状态表为空或者状态为空, flowExecuteId:{}", flowExecuteId);
            return;
        }

        String methodName = "收到数据回流状态发生变更通知, flowExecuteId:" + flowExecuteId + ", userId:" + dataBackflowStatus.getUserId() + ", projectId:" + dataBackflowStatus.getProjectId()
                + ", projectType:" + dataBackflowStatus.getProjectType() + ", dataType:" + dataBackflowStatus.getDataType() + ", typeDesc:" + DataBackflowStatusDataTypeEnum.getDesc(dataBackflowStatus.getDataType())
                + ", dataStatus:" + dataBackflowStatus.getDataStatus() + ", statusDesc:" + DataBackflowStatusDataStatusEnum.getDesc(dataBackflowStatus.getDataStatus());

        if (!HintManagerHolder.isMasterRouteOnly()) {
            HintManagerHolder.clear();
            HintManager hintManager = HintManager.getInstance();
            hintManager.setMasterRouteOnly();
        }
        ProjectFlowExecute projectFlowExecute = projectFlowExecuteMapperExt.selectByPrimaryKey(flowExecuteId);
        if (projectFlowExecute == null) {
            log.error(methodName + "-失败，ProjectFlowExecute记录不存在");
            return;
        }

        if (projectFlowExecute.getStatus() != null && ProjectFlowExecuteStatusEnum.EXECUTING.getStatus() != projectFlowExecute.getStatus().byteValue()) {
            log.info(methodName + "-标的申请流程已经完结, 不处理状态变更, 执行状态:{}", projectFlowExecute.getStatus());
            return;
        }

        if (DataBackflowStatusDataStatusEnum.BACKFLOW_SUCCESSED.getStatus().byteValue() == dataBackflowStatus.getDataStatus().byteValue()) {
            // 成功，判断是否所有（阻塞流程的回流）都成功，如果成功，结束当前流程，触发下一流程，否则，不处理
            boolean isAllSuccess = isAllBlockExecuteBackflowSuccess(projectFlowExecute.getBorrowUserId(), projectFlowExecute.getProjectId());
            if (isAllSuccess) {
                RLock lock = null;
                boolean isLock = false;
                try {
                    final String dataBackflowSuccessLock = DATA_BACKFLOW_SUCCESS_LOCK + flowExecuteId;
                    lock = redissonClient.getLock(dataBackflowSuccessLock);
                    isLock = dataBackflowSuccessLock(lock);
                    if (isLock) {
                        projectFlowExecuteService.initNextStepProjectFlowExecute(flowExecuteId, LoanNodeTypeEnum.LOAN_DATA_BACK_FLOW, LoanNodeTypeEnum.AUTH_STATUS_CHECK, LoanFlowMqConstant.Queue.AUTH_STATUS_CHECK_QUEUE);
                        log.info(methodName + "-所有信息回流成功，发送授权状态检测MQ成功");
                    } else {
                        log.warn(methodName + "-获取数据回流成功锁失败");
                    }
                } catch (Exception e) {
                    log.error(methodName + "初始化授权状态检测流程异常。请求信息:{}", e);
                } finally {
                    // 释放锁
//                    if (isLock) {
//                        dataBackflowSuccessUnLock(lock);
//                    }
                }
            } else {
                log.info(methodName + "-存在信息回流未完成，暂时不处理");
            }
        } else if (DataBackflowStatusDataStatusEnum.FAILED.getStatus().byteValue() == dataBackflowStatus.getDataStatus().byteValue()) {
            // 失败(超执行次数或者超时)，结束整个流程，退回标的
            log.info(methodName + "-失败(超执行次数或者超时)，结束整个流程，退回标的");
            failEndProjectFlowExecuteAndReturnProject(flowExecuteId, projectFlowExecute.getProjectId(), ReturnProjectReasonTypeEnum.enumOf(dataBackflowStatus.getDataType()), dataBackflowStatus.getDesc());
        } else {
            // 失败待重试(超执行次数或者超时)，不处理
            log.info(methodName + "-失败待重试(超执行次数或者超时)，更新流程为处理异常");
            projectFlowExecuteService.updateProjectFlowExecuteRuntime(flowExecuteId, LoanNodeTypeEnum.LOAN_DATA_BACK_FLOW, ProjectFlowExecuteRuntimeNodeStatusEnum.EXCEPTION, dataBackflowStatus.getDesc(), currentExecuteTimes);
        }

    }

    private boolean dataBackflowSuccessLock(RLock lock) {
        boolean tryLockResult = false;
        try {
            tryLockResult = lock.tryLock(-1, 30000, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            log.error("获取数据回流成功锁异常！", e);
        }
        return tryLockResult;
    }

    private void dataBackflowSuccessUnLock(RLock lock) {
        try {
            Optional.ofNullable(lock).ifPresent(l -> l.unlock());
        } catch (Exception e) {
            log.error("解锁数据回流成功锁异常！", e);
        }

    }

}
